package com.xh.hongrpc.fault.tolerant;

import cn.hutool.core.collection.CollUtil;
import com.xh.hongrpc.RpcApplication;
import com.xh.hongrpc.config.RpcConfig;
import com.xh.hongrpc.fault.retry.RetryStrategy;
import com.xh.hongrpc.fault.retry.RetryStrategyFactory;
import com.xh.hongrpc.loadbalancer.LoadBalancer;
import com.xh.hongrpc.loadbalancer.LoadBalancerFactory;
import com.xh.hongrpc.model.RpcRequest;
import com.xh.hongrpc.model.RpcResponse;
import com.xh.hongrpc.model.ServiceMetaInfo;
import com.xh.hongrpc.server.tcp.VertxTcpClient;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 转移到其他服务节点 - 容错策略
 */
@Slf4j
public class FailOverTolerantStrategy implements TolerantStrategy {

    @Override
    public RpcResponse doTolerant(Map<String, Object> context, Exception e) {
        RpcRequest rpcRequest = (RpcRequest) context.get("request");
        List<ServiceMetaInfo> serviceMetaInfoList = (List<ServiceMetaInfo>) context.get("serviceMetaInfoList");
        ServiceMetaInfo selectedServiceMetaInfo = (ServiceMetaInfo) context.get("selectedServiceMetaInfo");

        //移除失败节点
        removeFailedNode(serviceMetaInfoList,selectedServiceMetaInfo);

        RpcConfig rpcConfig = RpcApplication.getRpcConfig();
        LoadBalancer loadBalancer = LoadBalancerFactory.getInstance(rpcConfig.getLoadBalancer());
        Map<String, Object> requestParams = new HashMap<>();
        requestParams.put("methedName", rpcRequest.getMethodName());

        RpcResponse rpcResponse = null;
        while (serviceMetaInfoList.size()>0 || rpcResponse != null){
            ServiceMetaInfo currentServiceMetaInfo = loadBalancer.select(requestParams, serviceMetaInfoList);
            System.out.println("获取节点"+ currentServiceMetaInfo);
            try  {
                RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategy());
                rpcResponse = retryStrategy.doRetry(() -> VertxTcpClient.doRequest(rpcRequest, currentServiceMetaInfo));
                return rpcResponse;
            }catch (Exception exception){
                removeFailedNode(serviceMetaInfoList,currentServiceMetaInfo);
                continue;
            }
        }

        throw new RuntimeException(e);
    }

    private void removeFailedNode(List<ServiceMetaInfo> serviceMetaInfoList, ServiceMetaInfo currentServiceMetaInfo) {
        if (CollUtil.isNotEmpty(serviceMetaInfoList)){
            Iterator<ServiceMetaInfo> iterator = serviceMetaInfoList.iterator();
            while (iterator.hasNext()){
                ServiceMetaInfo serviceMetaInfo = iterator.next();
                if (serviceMetaInfo.getServiceNodeKey().equals(currentServiceMetaInfo.getServiceNodeKey())){
                    iterator.remove();
                }
            }
        }
    }
}
